package g06;

import java.util.List;
import java.util.Stack;

class State06 {
	private int cout;
	private int coutEstime;
	private int[][] taquin;
	private int taille;
	private int iCaseVide;
	private int jCaseVide;
	private State06 parent;
	private int[] perm;

	public State06(int[][] taquin, int cout, int iCaseVide, int jCaseVide,
			State06 parent, int[] perm) {
		this.taquin = taquin;
		this.taille = taquin.length - 1;
		this.cout = cout;
		this.coutEstime = -1;
		this.iCaseVide = iCaseVide;
		this.jCaseVide = jCaseVide;
		this.parent = parent;
		this.perm = perm;
	}

	public State06(int[][] taquin, int cout) {
		this.taquin = taquin;
		this.taille = taquin.length - 1;
		this.cout = cout;
		this.coutEstime = -1;
		for (int i = 0; i <= taille; i++) {
			for (int j = 0; j <= taille; j++) {
				if (taquin[i][j] == 0) {
					this.iCaseVide = i;
					this.jCaseVide = j;
				}
			}
		}
		parent = null;
	}

	public boolean isAGoal() {
		int max = ((taille + 1) * (taille + 1)) - 1;
		for (int i = taille; i >= 0; i--) {
			for (int j = taille; j >= 0; j--) {
				int t = taquin[i][j];
				if (t != max) {
					return false;
				}
				max--;
			}
		}
		return true;
	}

	public void heuristique() {
		this.coutEstime = 0;
		for (int i = 0; i <= taille; i++) {
			for (int j = 0; j <= taille; j++) {
				int ligne = (int) Math.floor((taquin[i][j]) / (taille + 1));
				int colonne = (int) Math.floor((taquin[i][j]) % (taille + 1));
				if (i != ligne || colonne != j) {
					int ligneEcart = Math.abs(i - ligne);
					int colEcart = Math.abs(j - colonne);
					coutEstime += (ligneEcart + colEcart);
				}
			}
		}
		// System.out.println("----------");
		// System.out.println(this);
		// System.out.println(this.coutEstime);

	}

	public int getCout() {
		return cout;
	}

	public int getCoutEstime() {
		return coutEstime;
	}

	public State06 getParent() {
		return parent;
	}

	public int[] getPerm() {
		return perm;
	}

	// renvoie les successeurs du state courant
	public Stack<State06> succState(List<State06> V) {
		Stack<State06> stackSucc = new Stack<State06>();
		// Case limitrophe gauche a la case vide
		int[] perm;
		if (jCaseVide != taille) {
			int[][] tt = createByClone();
			int[][] tTemp = swap(tt, iCaseVide, jCaseVide + 1);
			perm = new int[] { iCaseVide, jCaseVide + 1, iCaseVide, jCaseVide };
			State06 sTemp = new State06(tTemp, cout + 1, iCaseVide,
					jCaseVide + 1, this, perm);
			if (!V.contains(sTemp))
				stackSucc.add(sTemp);
		}
		// Case limitrophe droite a la case vide
		if (jCaseVide != 0) {
			perm = new int[] { iCaseVide, jCaseVide - 1, iCaseVide, jCaseVide };
			State06 sTemp = new State06(swap(createByClone(), iCaseVide,
					jCaseVide - 1), cout + 1, iCaseVide, jCaseVide - 1, this,
					perm);
			if (!V.contains(sTemp))
				stackSucc.add(sTemp);
		}
		// Case limitrophe bas a la case vide
		if (iCaseVide != 0) {
			perm = new int[] { iCaseVide - 1, jCaseVide, iCaseVide, jCaseVide };
			State06 sTemp = new State06(swap(createByClone(), iCaseVide - 1,
					jCaseVide), cout + 1, iCaseVide - 1, jCaseVide, this, perm);
			if (!V.contains(sTemp))
				stackSucc.add(sTemp);
		}
		// Case limitrophe haut a la case vide
		if (iCaseVide != taille) {
			perm = new int[] { iCaseVide + 1, jCaseVide, iCaseVide, jCaseVide };
			State06 sTemp = new State06(swap(createByClone(), iCaseVide + 1,
					jCaseVide), cout + 1, iCaseVide + 1, jCaseVide, this, perm);
			if (!V.contains(sTemp))
				stackSucc.add(sTemp);
		}
		return stackSucc;
	}

	public int[][] swap(int[][] newTaquin, int i, int j) {
		int valTemp = newTaquin[i][j];
		newTaquin[i][j] = 0;
		newTaquin[iCaseVide][jCaseVide] = valTemp;
		return newTaquin;
	}

	public int[][] createByClone() {
		int[][] newTaquin = taquin.clone();
		for (int i = 0; i < newTaquin.length; i++) {
			newTaquin[i] = newTaquin[i].clone();
		}
		return newTaquin;
	}

	@Override
	public boolean equals(Object stateP) {
		State06 state = (State06) stateP;
		for (int i = 0; i <= taille; i++) {
			for (int j = 0; j <= taille; j++) {
				if (taquin[i][j] != state.getTaquinByIndice(i, j))
					return false;
			}
		}
		return true;
	}

	public int getTaquinByIndice(int i, int j) {
		return taquin[i][j];
	}

	public String toString() {
		String s = "";
		for (int i = taille; i >= 0; i--) {
			for (int j = taille; j >= 0; j--) {
				if (taquin[i][j] > 9) {
					s += taquin[i][j] + " ";
				} else {
					s += taquin[i][j] + "  ";
				}
			}
			s += "\n";
		}
		return s;

	}
}
